<!DOCTYPE html>
<html lang="zh-cn"><head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    
    <link href="/css/bootstrap.min.css" rel="stylesheet">
    <link href="/css/main.css" rel="stylesheet">

    <link rel="shortcut icon" type="image/x-icon" href="/img/favicon.ico" />

    <title>Go程序的切片 | wiseAI的小站</title>
</head><body><div class="container-fluid">
    <nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand" href="/">
                <img src="/img/favicon.ico" alt="" width="30" height="24" class="d-inline-block align-text-top">
                wiseAI的小站
            </a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    
                    <li class="nav-item">
                        <a  class="nav-link"   href="/articles/">文章</a>
                    </li>
                    
                    <li class="nav-item">
                        <a  class="nav-link"   href="/categories/">分类</a>
                    </li>
                    
                    <li class="nav-item">
                        <a  class="nav-link"   href="/tags/">关键字</a>
                    </li>
                    
                    <li class="nav-item">
                        <a  class="nav-link"   href="/post/golang/">golang编程</a>
                    </li>
                    
                    <li class="nav-item">
                        <a  class="nav-link"   href="/about/">关于</a>
                    </li>
                    
                </ul>
                <form class="d-flex">
                    <input id="search-query" class="form-control me-2" type="search" placeholder="Search for anything..." aria-label="Search">
                </form>
            </div>
        </div>
    </nav>
</div>




<div id="content">
<div class="container article">
	<h1>Go程序的切片</h1>
	<div class="post-meta">
		<div>
			
			
			By wiseai | 2022-07-27 | 5 minutes
		</div>
		<div class="tags">
			
			<a class="btn btn-light links btn-sm" href="/tags/%E7%BC%96%E7%A8%8B/">编程</a>
			
		</div>
	</div>
	<div>
		<div class="article-post">
			<h1 id="72-切片">7.2 切片</h1>
<h2 id="721-概念">7.2.1 概念</h2>
<p>切片 (slice) 是对数组一个连续片段的引用（该数组我们称之为相关数组，通常是匿名的），所以切片是一个引用类型（因此更类似于 C/C++ 中的数组类型，或者 Python 中的 list 类型）。这个片段可以是整个数组，或者是由起始和终止索引标识的一些项的子集。需要注意的是，终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。</p>
<p>切片是可索引的，并且可以由 <code>len()</code> 函数获取长度。</p>
<p>给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是，切片的长度可以在运行时修改，最小为 0， 最大为相关数组的长度：切片是一个 <strong>长度可变的数组</strong>。</p>
<p>切片提供了计算容量的函数 <code>cap()</code> 可以测量切片最长可以达到多少：它等于切片的长度 + 数组除切片之外的长度。如果 <code>s</code> 是一个切片，<code>cap(s)</code> 就是从 <code>s[0]</code> 到数组末尾的数组长度。切片的长度永远不会超过它的容量，所以对于切片 <code>s</code> 来说该不等式永远成立：<code>0 &lt;= len(s) &lt;= cap(s)</code>。</p>
<p>多个切片如果表示同一个数组的片段，它们可以共享数据；因此一个切片和相关数组的其他切片是共享存储的，相反，不同的数组总是代表不同的存储。数组实际上是切片的构建块。</p>
<p><strong>优点</strong> 因为切片是引用，所以它们不需要使用额外的内存并且比使用数组更有效率，所以在 Go 代码中切片比数组更常用。</p>
<p>声明切片的格式是： <code>var identifier []type</code>（不需要说明长度）。</p>
<p>一个切片在未初始化之前默认为 <code>nil</code>，长度为 0。</p>
<p>切片的初始化格式是：<code>var slice1 []type = arr1[start:end]</code>。</p>
<p>这表示 <code>slice1</code> 是由数组 <code>arr1</code> 从 <code>start</code> 索引到 <code>end-1</code> 索引之间的元素构成的子集（切分数组，<code>start:end</code> 被称为切片表达式）。所以 <code>slice1[0]</code> 就等于 <code>arr1[start]</code>。这可以在 <code>arr1</code> 被填充前就定义好。</p>
<p>如果某个人写：<code>var slice1 []type = arr1[:]</code> 那么 <code>slice1</code> 就等于完整的 <code>arr1</code> 数组（所以这种表示方式是 <code>arr1[0:len(arr1)]</code> 的一种缩写）。另外一种表述方式是：<code>slice1 = &amp;arr1</code>。</p>
<p><code>arr1[2:]</code> 和 <code>arr1[2:len(arr1)]</code> 相同，都包含了数组从第三个到最后的所有元素。</p>
<p><code>arr1[:3]</code> 和 <code>arr1[0:3]</code> 相同，包含了从第一个到第三个元素（不包括第四个）。</p>
<p>如果你想去掉 <code>slice1</code> 的最后一个元素，只要 <code>slice1 = slice1[:len(slice1)-1]</code>。</p>
<p>一个由数字 1、2、3 组成的切片可以这么生成：<code>s := [3]int{1,2,3}[:]</code>（注：应先用 <code>s := [3]int{1, 2, 3}</code> 生成数组, 再使用 <code>s[:]</code> 转成切片）甚至更简单的 <code>s := []int{1,2,3}</code>。</p>
<p><code>s2 := s[:]</code> 是用切片组成的切片，拥有相同的元素，但是仍然指向相同的相关数组。</p>
<p>一个切片 <code>s</code> 可以这样扩展到它的大小上限：<code>s = s[:cap(s)]</code>，如果再扩大的话就会导致运行时错误（参见第 7.7 节）。</p>
<p>对于每一个切片（包括 <code>string</code>），以下状态总是成立的：</p>
<pre><code>s == s[:i] + s[i:] // i是一个整数且: 0 &lt;= i &lt;= len(s)
len(s) &lt;= cap(s)
</code></pre>
<p>切片也可以用类似数组的方式初始化：<code>var x = []int{2, 3, 5, 7, 11}</code>。这样就创建了一个长度为 5 的数组并且创建了一个相关切片。</p>
<p>切片在内存中的组织方式实际上是一个有 3 个域的结构体：指向相关数组的指针，切片长度以及切片容量。下图给出了一个长度为 2，容量为 4 的切片 <code>y</code>。</p>
<ul>
<li><code>y[0] = 3</code> 且 <code>y[1] = 5</code>。</li>
<li>切片 <code>y[0:4]</code> 由 元素 <code>3</code>，<code>5</code>，<code>7</code> 和 <code>11</code> 组成。</li>
</ul>
<img src="images/7.2_fig7.2.png?raw=true" style="zoom: 50%;" />
<p>示例 7.7 <a href="examples/chapter_7/array_slices.go">array_slices.go</a></p>
<pre tabindex="0"><code>package main
import &#34;fmt&#34;

func main() {
	var arr1 [6]int
	var slice1 []int = arr1[2:5] // item at index 5 not included!

	// load the array with integers: 0,1,2,3,4,5
	for i := 0; i &lt; len(arr1); i++ {
		arr1[i] = i
	}

	// print the slice
	for i := 0; i &lt; len(slice1); i++ {
		fmt.Printf(&#34;Slice at %d is %d\n&#34;, i, slice1[i])
	}

	fmt.Printf(&#34;The length of arr1 is %d\n&#34;, len(arr1))
	fmt.Printf(&#34;The length of slice1 is %d\n&#34;, len(slice1))
	fmt.Printf(&#34;The capacity of slice1 is %d\n&#34;, cap(slice1))

	// grow the slice
	slice1 = slice1[0:4]
	for i := 0; i &lt; len(slice1); i++ {
		fmt.Printf(&#34;Slice at %d is %d\n&#34;, i, slice1[i])
	}
	fmt.Printf(&#34;The length of slice1 is %d\n&#34;, len(slice1))
	fmt.Printf(&#34;The capacity of slice1 is %d\n&#34;, cap(slice1))

	// grow the slice beyond capacity
	//slice1 = slice1[0:7 ] // panic: runtime error: slice bound out of range
}
</code></pre><p>输出：</p>
<pre><code>Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
The length of arr1 is 6  
The length of slice1 is 3  
The capacity of slice1 is 4  
Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
Slice at 3 is 5  
The length of slice1 is 4  
The capacity of slice1 is 4  
</code></pre>
<p>如果 <code>s2</code> 是一个切片，你可以将 <code>s2</code> 向后移动一位 <code>s2 = s2[1:]</code>，但是末尾没有移动。切片只能向后移动，<code>s2 = s2[-1:]</code> 会导致编译错误。切片不能被重新分片以获取数组的前一个元素。</p>
<p><strong>注意</strong> 绝对不要用指针指向切片。切片本身已经是一个引用类型，所以它本身就是一个指针！！</p>
<p>问题 7.2： 给定切片 <code>b:= []byte{'g', 'o', 'l', 'a', 'n', 'g'}</code>，那么 <code>b[1:4]</code>、<code>b[:2]</code>、<code>b[2:]</code> 和 <code>b[:]</code> 分别是什么？</p>
<h2 id="722-将切片传递给函数">7.2.2 将切片传递给函数</h2>
<p>如果你有一个函数需要对数组做操作，你可能总是需要把参数声明为切片。当你调用该函数时，把数组分片，创建为一个切片引用并传递给该函数。这里有一个计算数组元素和的方法:</p>
<pre tabindex="0"><code>func sum(a []int) int {
	s := 0
	for i := 0; i &lt; len(a); i++ {
		s += a[i]
	}
	return s
}

func main() {
	var arr = [5]int{0, 1, 2, 3, 4}
	sum(arr[:])
}
</code></pre><h2 id="723-用-make-创建一个切片">7.2.3 用 make() 创建一个切片</h2>
<p>当相关数组还没有定义时，我们可以使用 <code>make()</code> 函数来创建一个切片，同时创建好相关数组：<code>var slice1 []type = make([]type, len)</code>。</p>
<p>也可以简写为 <code>slice1 := make([]type, len)</code>，这里 <code>len</code> 是数组的长度并且也是 <code>slice</code> 的初始长度。</p>
<p>所以定义 <code>s2 := make([]int, 10)</code>，那么 <code>cap(s2) == len(s2) == 10</code>。</p>
<p><code>make()</code> 接受 2 个参数：元素的类型以及切片的元素个数。</p>
<p>如果你想创建一个 <code>slice1</code>，它不占用整个数组，而只是占用以 <code>len</code> 为个数个项，那么只要：<code>slice1 := make([]type, len, cap)</code>。</p>
<p><code>make()</code> 的使用方式是：<code>func make([]T, len, cap)</code>，其中 <code>cap</code> 是可选参数。</p>
<p>所以下面两种方法可以生成相同的切片:</p>
<pre tabindex="0"><code>make([]int, 50, 100)
new([100]int)[0:50]
</code></pre><p>下图描述了使用 <code>make()</code> 方法生成的切片的内存结构：</p>
<img src="images/7.2_fig7.2.1.png?raw=true" style="zoom:50%;" />
<p>示例 7.8 <a href="examples/chapter_7/make_slice.go">make_slice.go</a></p>
<pre tabindex="0"><code>package main
import &#34;fmt&#34;

func main() {
	var slice1 []int = make([]int, 10)
	// load the array/slice:
	for i := 0; i &lt; len(slice1); i++ {
		slice1[i] = 5 * i
	}

	// print the slice:
	for i := 0; i &lt; len(slice1); i++ {
		fmt.Printf(&#34;Slice at %d is %d\n&#34;, i, slice1[i])
	}
	fmt.Printf(&#34;\nThe length of slice1 is %d\n&#34;, len(slice1))
	fmt.Printf(&#34;The capacity of slice1 is %d\n&#34;, cap(slice1))
}
</code></pre><p>输出：</p>
<pre><code>Slice at 0 is 0  
Slice at 1 is 5  
Slice at 2 is 10  
Slice at 3 is 15  
Slice at 4 is 20  
Slice at 5 is 25  
Slice at 6 is 30  
Slice at 7 is 35  
Slice at 8 is 40  
Slice at 9 is 45  

The length of slice1 is 10  
The capacity of slice1 is 10  
</code></pre>
<p>因为字符串是纯粹不可变的字节数组，它们也可以被切分成切片。</p>
<p>练习 7.4： <a href="examples/chapter_7/fibonacci_funcarray.go">fibonacci_funcarray.go</a>: 为练习 7.3 写一个新的版本，主函数调用一个使用序列个数作为参数的函数，该函数返回一个大小为序列个数的 Fibonacci 切片。</p>
<h2 id="724-new-和-make-的区别">7.2.4 new() 和 make() 的区别</h2>
<p>看起来二者没有什么区别，都在堆上分配内存，但是它们的行为不同，适用于不同的类型。</p>
<ul>
<li><code>new(T)</code> 为每个新的类型 <code>T</code> 分配一片内存，初始化为 <code>0</code> 并且返回类型为 <code>*T</code> 的内存地址：这种方法 <strong>返回一个指向类型为 <code>T</code>，值为 <code>0</code> 的地址的指针</strong>，它适用于值类型如数组和结构体（参见<a href="10.0.md">第 10 章</a>）；它相当于 <code>&amp;T{}</code>。</li>
<li><code>make(T)</code> <strong>返回一个类型为 T 的初始值</strong>，它只适用于 3 种内建的引用类型：切片、<code>map</code> 和 <code>channel</code>（参见<a href="08.0.md">第 8 章</a>和<a href="13.0.md">第 13 章</a>）。</li>
</ul>
<p>换言之，<code>new()</code> 函数分配内存，<code>make()</code> 函数初始化；下图给出了区别：</p>
<img src="images/7.2_fig7.3.png?raw=true" style="zoom:50%;" />
<p>在图 7.3 的第一幅图中：</p>
<pre tabindex="0"><code>var p *[]int = new([]int) // *p == nil; with len and cap 0
p := new([]int)
</code></pre><p>在第二幅图中， <code>p := make([]int, 0)</code> ，切片 已经被初始化，但是指向一个空的数组。</p>
<p>以上两种方式实用性都不高。下面的方法：</p>
<pre tabindex="0"><code>var v []int = make([]int, 10, 50)
</code></pre><p>或者</p>
<pre tabindex="0"><code>v := make([]int, 10, 50)
</code></pre><p>这样分配一个有 50 个 <code>int</code> 值的数组，并且创建了一个长度为 10，容量为 50 的切片 <code>v</code>，该切片指向数组的前 10 个元素。</p>
<p><strong>问题 7.3</strong> 给定 <code>s := make([]byte, 5)</code>，<code>len(s)</code> 和 <code>cap(s)</code> 分别是多少？<code>s = s[2:4]</code>，<code>len(s)</code> 和 <code>cap(s)</code> 又分别是多少？</p>
<p><strong>问题 7.4</strong> 假设 <code>s1 := []byte{'p', 'o', 'e', 'm'}</code> 且 <code>s2 := s1[2:]</code>，<code>s2</code> 的值是多少？如果我们执行 <code>s2[1] = 't'</code>，<code>s1</code> 和 <code>s2</code> 现在的值又分别是多少？</p>
<p><em>译者注：如何理解 new、make、slice、map、channel 的关系</em></p>
<p><em>1.slice、map 以及 channel 都是 golang 内建的一种引用类型，三者在内存中存在多个组成部分，
需要对内存组成部分初始化后才能使用，而 make 就是对三者进行初始化的一种操作方式</em></p>
<p><em>2. new 获取的是存储指定变量内存地址的一个变量，对于变量内部结构并不会执行相应的初始化操作，
所以 slice、map、channel 需要 make 进行初始化并获取对应的内存地址，而非 new 简单的获取内存地址</em></p>
<h2 id="725-多维切片">7.2.5 多维切片</h2>
<p>和数组一样，切片通常也是一维的，但是也可以由一维组合成高维。通过分片的分片（或者切片的数组），长度可以任意动态变化，所以 Go 语言的多维切片可以任意切分。而且，内层的切片必须单独分配（通过 <code>make()</code> 函数）。</p>
<h2 id="726-bytes-包">7.2.6 bytes 包</h2>
<p>类型 <code>[]byte</code> 的切片十分常见，Go 语言有一个 <code>bytes</code> 包专门用来提供这种类型的操作方法。</p>
<p><code>bytes</code> 包和字符串包十分类似（参见<a href="04.7.md">第 4.7 节</a>）。而且它还包含一个十分有用的类型 <code>Buffer</code>:</p>
<pre tabindex="0"><code>import &#34;bytes&#34;

type Buffer struct {
	...
}
</code></pre><p>这是一个长度可变的 <code>bytes</code> 的 buffer，提供 <code>Read()</code> 和 <code>Write()</code> 方法，因为读写长度未知的 <code>bytes</code> 最好使用 <code>buffer</code>。</p>
<p><code>Buffer</code> 可以这样定义：<code>var buffer bytes.Buffer</code>。</p>
<p>或者使用 <code>new()</code> 获得一个指针：<code>var r *bytes.Buffer = new(bytes.Buffer)</code>。</p>
<p>或者通过函数：<code>func NewBuffer(buf []byte) *Buffer</code>，创建一个 <code>Buffer</code> 对象并且用 <code>buf</code> 初始化好；<code>NewBuffer</code> 最好用在从 <code>buf</code> 读取的时候使用。</p>
<p><strong>通过 buffer 串联字符串</strong></p>
<p>类似于 Java 的 StringBuilder 类。</p>
<p>在下面的代码段中，我们创建一个 <code>buffer</code>，通过 <code>buffer.WriteString(s)</code> 方法将字符串 <code>s</code> 追加到后面，最后再通过 <code>buffer.String()</code> 方法转换为 <code>string</code>：</p>
<pre tabindex="0"><code>var buffer bytes.Buffer
for {
	if s, ok := getNextString(); ok { //method getNextString() not shown here
		buffer.WriteString(s)
	} else {
		break
	}
}
fmt.Print(buffer.String(), &#34;\n&#34;)
</code></pre><p>这种实现方式比使用 <code>+=</code> 要更节省内存和 CPU，尤其是要串联的字符串数目特别多的时候。</p>
<p><strong>练习 7.5</strong></p>
<p>给定切片 <code>sl</code>，将一个 <code>[]byte</code> 数组追加到 <code>sl</code> 后面。写一个函数 <code>Append(slice, data []byte) []byte</code>，该函数在 <code>sl</code> 不能存储更多数据的时候自动扩容。</p>
<p><strong>练习 7.6</strong></p>
<p>把一个缓存 <code>buf</code> 分片成两个切片：第一个是前 <code>n</code> 个 bytes，后一个是剩余的，用一行代码实现。</p>

		</div>
	</div>
</div>


<div class="container">
    
    <div class="row">
        
        <div class="col-5">
            <a class="page-link link-dark text-end dh" href="/post/golang/2022.07.26-%E6%95%B0%E7%BB%84%E5%A3%B0%E6%98%8E%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96/">
                <h5>前一篇</h5><br>
                Go程序的数组声明和初始化
            </a>            
        </div>
        
        <div class="col-2">
        </div>
        
        <div class="col-5">
            <a class="page-link link-dark text-start dh" href="/post/golang/2022.07.28-for-range%E7%BB%93%E6%9E%84/">
                <h5>后一篇</h5><br>
                Go程序的for Range结构
            </a>            
        </div>
        
    </div>
    
</div>

        </div><br><br>
<footer class="container">
    <h2>友情链接</h2>
    <hr>
    <nav class="nav nav-pills flex-column flex-sm-row">
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://wiseai.gitee.io/pages/gnzg/index.html" target="_blank">孤鸟之歌</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://wiseai.gitee.io/pages/mm/index.html" target="_blank">生成随机字符</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://wiseai.gitee.io/pages/md/index.html" target="_blank">MarkDown</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://gitee.com/" target="_blank">Gitee</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://github.com/" target="_blank">Github</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.aliyun.com/" target="_blank">阿里云</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://cloud.tencent.com/" target="_blank">腾讯云</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.oschina.net/" target="_blank">OSCHINA开源中国</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://gitee.com/wiseai/the-way-to-go_ZH_CN/blob/master/eBook/directory.md" target="_blank">the way to go</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://topgoer.com/" target="_blank">golang文档</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://goframe.org/display/gf" target="_blank">GoFrame</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.aliyundrive.com/" target="_blank">阿里云盘</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://cn.vuejs.org/" target="_blank">vue3文档</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://element-plus.gitee.io/zh-CN/" target="_blank">element-plus文档</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.runoob.com/vue3/vue3-tutorial.html" target="_blank">vue3菜鸟教程</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://v5.bootcss.com/" target="_blank">bootstrap v5 中文文档</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.bootstrap.cn/" target="_blank">bootstrap文档</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://caddy2.dengxiaolong.com/docs/" target="_blank">caddy中文教程</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.58pic.com/" target="_blank">千图</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://ifonts.com/" target="_blank">iFonts</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://marketing.qiniu.com/" target="_blank">七牛云</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.ixigua.com/" target="_blank">西瓜视频</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://wiseai.gitee.io/pages/yugang/index.html" target="_blank">鱼缸计算</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://justcc.mengkang.net/#/" target="_blank">C语言JustCC</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://wiseai.gitee.io/pages/pptist/" target="_blank">PPTist</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="/index.xml" target="_blank">RSS</a>
        
        <a class="flex-sm-fill text-sm-center nav-link links link-dark" href="https://www.kancloud.cn/idcpj/python/418553" target="_blank">参考资料</a>
        
    </nav>

    <div class="copyright text-center">
      <span class="power-by">
        Powered by <a class="links" href="https://gohugo.io" target="_blank">Hugo</a>
    </span>
    <span>|</span>
    <span>
        Theme - <a class="links" href="https://github.com/wiseai-go/blog-hugo" target="_blank">WiseAI</a>
    </span>
    <br>
    <span class="copyright-year">
        &copy;
        
        2017 -
        2023<span>
            陇ICP备15000157号
            
        </span></span>

</div>
</footer>
<script src="/js/bootstrap.bundle.min.js"></script>


</body>
</html>
